
// ===============================================================================================================
// -*- C++ -*-
//
// Camera.cpp - A first person style 3D camera.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include "Camera.hpp"

Camera::Camera(void)
: right(1.0f, 0.0f, 0.0f), up(0.0f, 1.0f, 0.0f), forward(0.0f, 0.0f, 1.0f), eye(0.0f, 15.0f, -25.0f)
{
	// Initialize to a default position (0, 15, -25)
}

Camera::Camera(const Vec3f & rightVec, const Vec3f & upVec, const Vec3f & forwardVec, const Vec3f & eyeVec)
: right(rightVec), up(upVec), forward(forwardVec), eye(eyeVec)
{
}

void Camera::Reset(const Vec3f & rightVec, const Vec3f & upVec, const Vec3f & forwardVec, const Vec3f & eyeVec)
{
	right = rightVec;
	up = upVec;
	forward = forwardVec;
	eye = eyeVec;
}

void Camera::Pitch(float angle)
{
	// Calculate new forward:
	RotateAroundAxis(forward, forward, right, angle);

	// Calculate new camera up vector:
	up = forward.CrossProduct(right);
}

void Camera::Rotate(float angle)
{
	float xxx, zzz;
	float sinAng, cosAng;

	MathLib::SineCosine(angle, sinAng, cosAng);

	// Save off forward components for computation
	xxx = forward.x;
	zzz = forward.z;

	// Rotate forward vector
	forward.x = xxx *  cosAng + zzz * sinAng;
	forward.z = xxx * -sinAng + zzz * cosAng;

	// Save off up components for computation
	xxx = up.x;
	zzz = up.z;

	// Rotate up vector
	up.x = xxx *  cosAng + zzz * sinAng;
	up.z = xxx * -sinAng + zzz * cosAng;

	// Save off right components for computation
	xxx = right.x;
	zzz = right.z;

	// Rotate right vector
	right.x = xxx *  cosAng + zzz * sinAng;
	right.z = xxx * -sinAng + zzz * cosAng;
}

void Camera::Move(MoveDir dir, float amount, float x, float y, float z)
{
	switch (dir)
	{
	case Camera::FORWARD: // Move along the camera's forward vector:
		eye.x += (forward.x * amount) * x;
		eye.y += (forward.y * amount) * y;
		eye.z += (forward.z * amount) * z;
		break;

	case Camera::BACK: // Move along the camera's negative forward vector:
		eye.x -= (forward.x * amount) * x;
		eye.y -= (forward.y * amount) * y;
		eye.z -= (forward.z * amount) * z;
		break;

	case Camera::LEFT : // Move along the camera's negative right vector:
		eye.x += (right.x * amount) * x;
		eye.y += (right.y * amount) * y;
		eye.z += (right.z * amount) * z;
		break;

	case Camera::RIGHT: // Move along the camera's right vector:
		eye.x -= (right.x * amount) * x;
		eye.y -= (right.y * amount) * y;
		eye.z -= (right.z * amount) * z;
		break;

	default: // Invalid argument!
		break;
	}
}

void Camera::RotateAroundAxis(Vec3f & result, const Vec3f & vec, const Vec3f & axis, float angle)
{
	float sinAng, cosAng;
	MathLib::SineCosine(angle, sinAng, cosAng);

	const float oneMinusCosAng = (1.0f - cosAng);
	const float aX = axis.x;
	const float aY = axis.y;
	const float aZ = axis.z;

	// Calculate X component:
	float xxx = (aX * aX * oneMinusCosAng + cosAng)      * vec.x +
				(aX * aY * oneMinusCosAng + aZ * sinAng) * vec.y +
				(aX * aZ * oneMinusCosAng - aY * sinAng) * vec.z;

	// Calculate Y component:	   
	float yyy = (aX * aY * oneMinusCosAng - aZ * sinAng) * vec.x +
				(aY * aY * oneMinusCosAng +      cosAng) * vec.y +
				(aY * aZ * oneMinusCosAng + aX * sinAng) * vec.z;

	// Calculate Z component:
	float zzz = (aX * aZ * oneMinusCosAng + aY * sinAng) * vec.x +
				(aY * aZ * oneMinusCosAng - aX * sinAng) * vec.y +
				(aZ * aZ * oneMinusCosAng + cosAng)      * vec.z;

	result.x = xxx;
	result.y = yyy;
	result.z = zzz;
}